import * as THREE from 'three';
import { createCameraManager } from '@/camera.js';
import { createAssetInstance } from '@/assets.js';

export function createScene() {
  const gameWindow = document.getElementById('render-target');
  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0x777777);

  const cameraManager = createCameraManager(gameWindow);

  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(gameWindow.offsetWidth, gameWindow.offsetHeight);
  renderer.setClearColor(0x000000, 0);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  gameWindow.appendChild(renderer.domElement);

  const raycaster = new THREE.Raycaster();
  const mouse = new THREE.Vector2();

  /** 用户最后点过的东西 */
  let activeObject = void 0;

  let hoverObject = void 0;
  
  /** @type {undefined | THREE.Mesh<THREE.BoxGeometry, THREE.MeshLambertMaterial, THREE.Object3DEventMap>} */
  let selectedObject = void 0;

  /** @type {ReturnType<createAssetInstance>[][]} */
  let buildings = [];

  /**
   * @param {ReturnType<import('@/city.js').createCity>} city 
   */
  function initialize(city) {
    scene.clear();
    buildings = [];
    for (let x = 0; x < city.size; x++) {
      const column = [];
      for (let y = 0; y < city.size; y++) {
        // 1. 加载 {x,y} 位置的贴图对应的 网格/3D 对象
        // 2. 将网格添加到场景中
        // 3. 将该网格添加到网格数组中

        // Grass geometry
        const mesh = createAssetInstance(city.tiles[x][y].terrainId, x, y);
        scene.add(mesh);
        column.push(mesh);
      }
      buildings.push([...Array.from({ length: city.size })]);
    }

    setupLights();
  }

  /**
   * @param {ReturnType<import('@/city.js').createCity>} city 
   */
  function update(city) {
    for (let x = 0; x < city.size; x++) {
      for (let y = 0; y < city.size; y++) {
        const tile = city.tiles[x][y];
        const existingBuildingMesh = buildings[x][y];

        // 若玩家移除一座建筑，则将此建筑从场景中移除
        if (!tile.building && existingBuildingMesh) {
          scene.remove(existingBuildingMesh);
          buildings[x][y] = void 0;
        }

        // 若数据模型改变，更新网格
        if (tile.building && tile.building.updated) {
          scene.remove(existingBuildingMesh);
          buildings[x][y] = createAssetInstance(tile.building.type, x, y, tile.building);
          scene.add(buildings[x][y]);
          tile.building.updated = false;
        }
      }
    }
  }

  function setupLights() {
    const sun = new THREE.DirectionalLight(0xffffff, 1);
    sun.position.set(20, 20, 20);
    sun.castShadow = true;
    sun.shadow.camera.left = -10;
    sun.shadow.camera.right = 10;
    sun.shadow.camera.top = 0;
    sun.shadow.camera.bottom = -10;
    sun.shadow.mapSize.width = 1024;
    sun.shadow.mapSize.height = 1024;
    sun.shadow.camera.near = .5;
    sun.shadow.camera.far = 50;
    scene.add(sun);
    scene.add(new THREE.AmbientLight(0xffffff, .3));
    // const helper = new THREE.CameraHelper(sun.shadow.camera);
    // scene.add(helper);
  }

  function draw() {
    renderer.render(scene, cameraManager.camera);
  }

  function start() {
    renderer.setAnimationLoop(draw);
  }

  function stop() {
    renderer.setAnimationLoop(null);
  }

  /**
   * 调整渲染器大小以适应当前游戏窗口
   */
  function handleResize() {
    cameraManager.camera.aspect = gameWindow.offsetWidth / gameWindow.offsetHeight;
    cameraManager.camera.updateProjectionMatrix();
    renderer.setSize(gameWindow.offsetWidth, gameWindow.offsetHeight);
  }

  function setObjectEmission(object, color) {}

  /**
   * 设置当前突出显示的对象
   * @param {THREE.Mesh} object 
   */
  function setHighlightedObject(object) {
    // 取消选中之前被选中的对象（如果该对象当前未被选中）
    if (hoverObject && hoverObject != activeObject) {
      setObjectEmission(hoverObject, 0x000000);
    }

    hoverObject = object;

    if (hoverObject) {
      // 重点显示当前处于悬停状态的新对象（如果该对象当前尚未被选中）
      setObjectEmission(hoverObject, 0x555555);
    }
  }

  /**
   * 获取当前鼠标指针所指的对象，若未指到对象则返回 null
   * @param {MouseEvent} event 
   */
  function getSelectedObject(event) {
    mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
    mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, cameraManager.camera);

    let intersections = raycaster.intersectObjects(scene.children, false);
    if (0 < intersections.length) {
      return intersections[0].object;
    } else {
      return null;
    }
  }

  /**
   * 设置当前显示对象并突出显示
   * @param {object} object 
   */
  function setActiveObject(object) {
    // 旧对象去掉高亮
    setObjectEmission(activeObject, 0x000000);
    activeObject = object;
    // 现对象添加高亮
    setObjectEmission(activeObject, 0xaaaa55);
  }

  /**
   * @param {MouseEvent} event 
   */
  function handleMouseUp(event) {
    cameraManager.handleMouseUp(event);
  }

  /**
   * @param {MouseEvent} event 
   */
  function handleMouseMove(event) {
    cameraManager.handleMouseMove(event);
  }

  return {
    cameraManager,
    initialize,
    update,
    start,
    stop,
    handleResize,
    setHighlightedObject,
    getSelectedObject,
    setActiveObject,
    handleMouseUp,
    handleMouseMove,
  };
}
